﻿#region Using Statements
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Neat;
using Neat.Mathematics;
using Neat.GUI;
using Neat.Graphics;
using Sectors;
using Microsoft.Xna.Framework.Input;
using System;
using System.Diagnostics;
using System.Windows.Forms;
using System.IO;
using System.Collections.Generic;
using SectorsDesigner.MapMaker;

using Keys = Microsoft.Xna.Framework.Input.Keys;
#endregion

namespace SectorsDesigner
{
    public partial class SectorsMapMakerControl : GraphicsDeviceControl
    {
        public List<Vector2> Vertices = new List<Vector2>();
        public List<Edge> Edges = new List<Edge>();
        public List<SectorInfo> Sectors = new List<SectorInfo>();
        public List<PolygonColorTexture> Polys = new List<PolygonColorTexture>();
        public List<int> DeletedEdges = new List<int>();
        public List<int> DeletedVertices = new List<int>();
        public int CurrentVertexIndex = -1;
        public int CurrentEdgeIndex
        {
            get
            {
                if (SelectedEdgesIndices == null || SelectedEdgesIndices.Count == 0)
                    return -1;
                return SelectedEdgesIndices[0];
            }
            set
            {
                SelectedEdgesIndices.Clear();
                if (value >= 0)
                    SelectedEdgesIndices.Add(value);
            }
        }
        public int CurrentSectorIndex
        {
            get
            {
                if (SelectedSectorsIndices == null || SelectedSectorsIndices.Count == 0)
                    return -1;
                return SelectedSectorsIndices[0];
            }
            set
            {
                SelectedSectorsIndices.Clear();
                if (value >= 0)
                    SelectedSectorsIndices.Add(value);
            }
        }
        public int LastVertexIndex = -1;
        public Mesh Dummy;
        public SectorInfo DummySector = new SectorInfo();
        public Wall DummyWall = new Wall();
        public List<int> SelectedSectorsIndices = new List<int>();
        public List<int> SelectedEdgesIndices = new List<int>();
        public List<int> AliveVertices
        {
            get
            {
                List<int> v = new List<int>();
                for (int i = 0; i < Vertices.Count; i++)
                {
                    if (DeletedVertices.Contains(i)) continue;
                    v.Add(i);
                }
                return v;
            }
        }
        public List<int> AliveEdges
        {
            get
            {
                List<int> e = new List<int>();
                for (int i = 0; i < Edges.Count; i++)
                {
                    if (DeletedEdges.Contains(i)) continue;
                    e.Add(i);
                }
                return e;
            }
        }

        public float TextureMoveSpeed = 0.0005f;
        public float VertexMoveSpeed = 0.1f;

        public void New()
        {
            FileName = null;
            World = new World();
            Vertices = new List<Vector2>();
            Edges = new List<Edge>();
            Sectors = new List<SectorInfo>();
            Polys = new List<PolygonColorTexture>();
            DeletedEdges = new List<int>();
            DeletedVertices = new List<int>();
            CurrentVertexIndex = -1;
            CurrentEdgeIndex = -1;
            CurrentSectorIndex = -1;
            LastVertexIndex = -1;
            Dummy = new MapMaker.Mesh(Edges); 
            DummySector = new SectorInfo();
            DummyWall = new Wall();

            //From NeatMapMaker.cs
            EditMode = EditModes.NewSector;
            SelectedSector = null;
            SelectedWall = null;
            LastVertex = new Vector2();
            CurrentPolygon = new Polygon();
        }

        void ShiftSectorTexture(Vector2 uv)
        {
            if (SectorFillMode == SectorFillModes.Wire) return;
            foreach (var item in SelectedSectorsIndices)
            {
                if (SectorFillMode == SectorFillModes.Ceiling)
                    Sectors[item].CeilingUV += uv;
                else
                    Sectors[item].FloorUV += uv;
            }
        }

        public void RemoveSectors()
        {
            //TODO: Implement RemoveSectors()
        }

        void UpdateGame()
        {
            var KeyboardState = Keyboard.GetState();
            if (KeyboardState.IsKeyDown(Keys.NumPad4)) offset.X--;
            if (KeyboardState.IsKeyDown(Keys.NumPad6)) offset.X++;
            if (KeyboardState.IsKeyDown(Keys.NumPad8)) offset.Y--;
            if (KeyboardState.IsKeyDown(Keys.NumPad2)) offset.Y++;

            if (KeyboardState.IsKeyDown(Keys.LeftAlt) || KeyboardState.IsKeyDown(Keys.RightAlt))
            {
            }
            switch (EditMode)
            {
                case EditModes.SelectSector:
                    {
                        if (CurrentSectorIndex < 0) break;
                        if (KeyboardState.IsKeyDown(Keys.Right)) ShiftSectorTexture(-Vector2.UnitX * TextureMoveSpeed);
                        else if (KeyboardState.IsKeyDown(Keys.Left)) ShiftSectorTexture(Vector2.UnitX * TextureMoveSpeed);
                        if (KeyboardState.IsKeyDown(Keys.Up)) ShiftSectorTexture(-Vector2.UnitY * TextureMoveSpeed);
                        else if (KeyboardState.IsKeyDown(Keys.Down)) ShiftSectorTexture(Vector2.UnitY * TextureMoveSpeed);
                    }
                    break;
                case EditModes.SelectEdge:
                    {
                        if (KeyboardState.IsKeyDown(Keys.D0)) ChangeEdgesType(0);
                        if (KeyboardState.IsKeyDown(Keys.D1)) ChangeEdgesType(1);
                        if (KeyboardState.IsKeyDown(Keys.D2)) ChangeEdgesType(2);
                        if (KeyboardState.IsKeyDown(Keys.D3)) ChangeEdgesType(3);
                    }
                    break;
                case EditModes.SelectVertex:
                    {
                        if (CurrentVertexIndex < 0) break;
                        if (KeyboardState.IsKeyDown(Keys.Left))
                            MoveVertex(CurrentVertexIndex, -Vector2.UnitX * VertexMoveSpeed);
                        else if (KeyboardState.IsKeyDown(Keys.Right))
                            MoveVertex(CurrentVertexIndex, Vector2.UnitX * VertexMoveSpeed);
                        if (KeyboardState.IsKeyDown(Keys.Up))
                            MoveVertex(CurrentVertexIndex, -Vector2.UnitY * VertexMoveSpeed);
                        else if (KeyboardState.IsKeyDown(Keys.Down))
                            MoveVertex(CurrentVertexIndex, Vector2.UnitY * VertexMoveSpeed);
                    }
                    break;
            }
        }

        void MoveVertex(int vi, Vector2 offset)
        {
            Vertices[vi] += offset;
            for (int si = 0; si < Sectors.Count; si++)
            {
                if (Sectors[si].Mesh.VertexIndices.Contains(vi))
                {
                    Polys[si] = new PolygonColorTexture(new Polygon(Sectors[si].Mesh.ToVertexList()), Polys[si].Color, Polys[si].Texture);
                }
            }
        }

        void ChangeEdgesType(int typeNo)
        {
            foreach (var item in SelectedEdgesIndices)
            {
                Edges[item].Wall.WallType = (Sectors.Wall.WallTypes)typeNo;
            }
        }

        List<Vector2> newPolygonVertices = new List<Vector2>();
        void NewSectorAddVertex(Vector2 m)
        {
            if (newPolygonVertices.Contains(m))
            {
                //Poly contains m. either close the poly, or cancel the current vertex
                if (m != newPolygonVertices[0]) //Cancel it.
                    return;
                else
                {
                    //Close the poly.
                    NewSectorFinish();
                    return;
                }
            }
            else
                newPolygonVertices.Add(m);
#if COMMENT
            if (!Vertices.Contains(m)) //Vertex is new
            {
                //Vertices.Add(m);
                SplitWalls(m, true);
                CurrentVertexIndex = Vertices.Count - 1;
            }
            else
            {
                CurrentVertexIndex = Vertices.IndexOf(m);
                if (DeletedVertices.Contains(CurrentVertexIndex)) 
                    DeletedVertices.Remove(CurrentVertexIndex);

                if (Dummy.VertexIndices.Contains(CurrentVertexIndex)) //Vertex is used in dummy
                {
                    if (CurrentVertexIndex != Edges[Dummy.Indices[0]].Indices[0]) //but Vertex is not dummy's starting point?
                    {
                        throw new Exception("Invalid polygon.");
                    }
                }
            }

            if (LastVertexIndex >= 0) //Vertex is not dummy's first
            {
                Edge e = new Edge(Vertices, LastVertexIndex, CurrentVertexIndex, DummyWall); //Create a new edge
                Edge s = Edges.Find(o => o.Equals(e));

                if (s != null) //Edge already exists
                {
                    CurrentEdgeIndex = Edges.IndexOf(s);
                    if (DeletedEdges.Contains(CurrentEdgeIndex))
                    {
                        DeletedEdges.Remove(CurrentEdgeIndex);
                        for (int i = 0; i < Edges[CurrentEdgeIndex].Indices.Count; i++)
                        {
                            if (DeletedVertices.Contains(Edges[CurrentVertexIndex].Indices[i]))
                                DeletedVertices.Remove(Edges[CurrentVertexIndex].Indices[i]);
                        } 
                    }
                }
                else
                {
                    e.Wall = Wall.FromWall(DummyWall, new LineSegment(Vertices[LastVertexIndex], Vertices[CurrentVertexIndex])); //didn't want to do this earlier, to make sure that we're gonna use this wall.
                    Edges.Add(e);
                    FixEdges();
                    CurrentEdgeIndex = Edges.Count - 1;
                }

                if (!Dummy.AddEdge(CurrentEdgeIndex)) MessageBox.Show("Edge was not added");
                
                if (CurrentVertexIndex == Edges[Dummy.Indices[0]].Indices[0])
                    CloseDummy();
            }

            LastVertexIndex = CurrentVertexIndex;
            LastVertex = m;
#endif
        }

        void FixEdges()
        {
            bool bk = false;
            foreach (var ei in AliveEdges)
            {
                foreach (var oi in AliveEdges)
                {
                    bk = false;
                    if (ei == oi || Edges[ei].Equals(Edges[oi])) continue;
                    var esegs = Edges[ei].ToSegments();
                    var osegs = Edges[oi].ToSegments();
                    
                    var ip = new Vector2(float.NaN);
                    foreach (var es in esegs)
                    {
                        if (es.StartPos == es.EndPos) continue;

                        foreach (var os in osegs)
                        {
                            if (os.StartPos == os.EndPos) continue;

                            ip = es.GetIntersectionPoint(os);
                            if (!GeometryHelper.IsNaN(ip))
                            {
                                bk = true;
                                break;
                            }
                        }
                        if (bk) break;
                    }
                    if (!GeometryHelper.IsNaN(ip)) //lines intersect
                    {
                        var vi = Vertices.Count;
                        if (Vertices.Contains(ip))
                        {
                            vi = Vertices.IndexOf(ip);
                            if (Edges[ei].Indices.Contains(vi) && Edges[oi].Indices.Contains(vi))
                                break;

                            if (DeletedVertices.Contains(vi)) DeletedVertices.Remove(vi);

                            if (Edges[ei].Indices.Contains(vi))
                            {
                                //V is either the head or tail of E, but O doesn't contain vi
                                //split O from V, leave E alone
                                SplitEdges(oi, vi);
                                break;
                            }
                            else if (Edges[oi].Indices.Contains(vi))
                            {
                                //V is either the head or tail of O, but E doesn't contain vi
                                //split E from V, leave O alone
                                SplitEdges(ei, vi);
                                break;
                            }
                            else
                            {
                                //Neither E nor O contain vi. must split both from V

                            }
                        }
                        else
                            Vertices.Add(ip);

                        SplitEdges(oi, vi);
                        SplitEdges(ei, vi);
                    }
                }
            }
        }

        void SplitEdges(int ei, int vi)
        {
            Vector2 ip = Vertices[vi];
            Edge e2 = new Edge(Vertices, vi, Edges[ei].LastIndex, Wall.FromWall(Edges[ei].Wall, new LineSegment(ip, Edges[ei].LastVertex)));
            Edge e1 = new Edge(Vertices, Edges[ei].FirstIndex, vi, Wall.FromWall(Edges[ei].Wall, new LineSegment(Edges[ei].FirstVertex, ip)));
            
            Edge ec1 = Edges.Find(o => o.Equals(e1));
            Edge ec2 = Edges.Find(o => o.Equals(e2));

            
            if (ec1 != null)
            {
                int eci1 = Edges.IndexOf(ec1);
                if (DeletedEdges.Contains(eci1)) DeletedEdges.Remove(eci1);
                for (int si = 0; si < Sectors.Count; si++)
                {
                    if (Sectors[si].Mesh.Indices.Contains(ei))
                    {
                        Sectors[si].Mesh.Indices[Sectors[si].Mesh.Indices.IndexOf(ei)] = eci1;
                    }
                }
                if (Dummy.Indices.Contains(ei)) Dummy.Indices[Dummy.Indices.IndexOf(ei)] = eci1;
                DeletedEdges.Add(ei);
                ei = eci1;
            }
            else
            {
                Edges[ei] = e1;
            }

            int eci2 = Edges.Count;
            if (ec2 != null)
            {
                eci2 = Edges.IndexOf(ec2);
                if (DeletedEdges.Contains(eci2)) DeletedEdges.Remove(eci2);
            }
            else 
                Edges.Add(e2);
            for (int si = 0; si < Sectors.Count; si++)
            {
                if (Sectors[si].Mesh.Indices.Contains(ei))
                {
                    Sectors[si].Mesh.Indices.Insert(Sectors[si].Mesh.Indices.IndexOf(ei) + 1, eci2);
                    Polys[si] = new PolygonColorTexture(new Polygon(Sectors[si].Mesh.ToVertexList()), Polys[si].Color, Polys[si].Texture);
                }
            }
            if (Dummy.Indices.Contains(ei)) Dummy.Indices.Insert(Dummy.Indices.IndexOf(ei)+1, eci2);
        }

        void NewSectorBackOff() //Remove the last vertex if possible.
        {
            if (newPolygonVertices.Count > 0) newPolygonVertices.RemoveAt(newPolygonVertices.Count - 1);
#if COMMENT
            //TODO: Fix bugs
            if (LastVertexIndex == -1) return;
            List<int> es = new List<int>();
            for (int i = 0; i < Edges.Count; i++)
            {
                if (Edges[i].Indices.Contains(LastVertexIndex))
                {
                    es.Add(i);
                    break;
                }
            }
            if (es.Count == 1)
            {
                LastVertexIndex = Edges[es[0]].Indices[Edges[es[0]].Indices.Count - 2];
                Dummy.Indices.Remove(es[0]);
                DeletedEdges.Add(es[0]);
            }
            //if (es.Count < 1)
            //{
                //LastVertexIndex = Edges[CurrentEdgeIndex].Indices[Edges[CurrentEdgeIndex].Indices.Count - 2];
            //}
#endif
        }

        void NewSectorFinish()
        {
            if (newPolygonVertices.Count < 2)
            {
                MessageBox.Show("Invalid Polygon");
                return;
            }
            CloseDummy();
#if COMMENT
            if (Dummy.Indices.Count < 2)
            {
                MessageBox.Show("Invalid Polygon");
                return;
            }

            int hI, tI;
            Dummy.GetHeadAndTail(out hI, out tI);

            if (hI < 0 || tI < 0)
            {
                MessageBox.Show("Cannot find head or tail.");
                return;
            }
            LineSegment ls = new LineSegment(Vertices[hI], Vertices[tI]);
            
            if (Dummy.ToSegments().Find(o => o == ls) == null)
            {
                NewSectorAddVertex(Vertices[hI]);
            }
            else 
                CloseDummy();
            //LastVertex = Vertices[hI];
#endif
        }

        void CloseDummy() //means switch dummy polygon/sector/... with a new one
        {
            newPolygonVertices = new Polygon(newPolygonVertices).GetVerticesClockwise();
            newPolygonVertices.Add(newPolygonVertices[0]);
            for (int i = 0; i < newPolygonVertices.Count; i++)
            {
                var m = newPolygonVertices[i];
                if (!Vertices.Contains(m))
                {
                    SplitWalls(m, true);
                    CurrentVertexIndex = Vertices.Count - 1;
                }
                else
                {
                    CurrentVertexIndex = Vertices.IndexOf(m);
                    if (DeletedVertices.Contains(CurrentVertexIndex)) DeletedVertices.Remove(CurrentVertexIndex);
                }

                if (LastVertexIndex >= 0)
                {
                    Edge e = new Edge(Vertices, LastVertexIndex, CurrentVertexIndex, DummyWall);
                    Edge s = Edges.Find(o => o.Equals(e));

                    if (s != null) //Edge exists
                    {
                        CurrentEdgeIndex = Edges.IndexOf(s);
                        if (DeletedEdges.Contains(CurrentEdgeIndex))
                        {
                            DeletedEdges.Remove(CurrentEdgeIndex);
                            for (int j = 0; j < Edges[CurrentEdgeIndex].Indices.Count; j++)
                            {
                                if (DeletedVertices.Contains(Edges[CurrentVertexIndex].Indices[j]))
                                    DeletedVertices.Remove(Edges[CurrentVertexIndex].Indices[j]);
                            }
                        }
                    }
                    else
                    {
                        e.Wall = Wall.FromWall(DummyWall,
                            new LineSegment(Vertices[LastVertexIndex], Vertices[CurrentVertexIndex]));
                        Edges.Add(e);
                        FixEdges();
                        CurrentEdgeIndex = Edges.Count - 1;
                    }

                    if (!Dummy.AddEdge(CurrentEdgeIndex)) MessageBox.Show("Edge was not added");
                }

                LastVertexIndex = CurrentVertexIndex;
                LastVertex = m;
            }

            if (Dummy.Indices.Count >= 3)
            {
                DummySector.Mesh = Dummy;
                DummySector.Name = "sector" + this.Sectors.Count.ToString();
                Sectors.Add(DummySector);
                AddPoly(new Polygon(Dummy.ToVertexList()));
            }
            else ;// MessageBox.Show("A sector must contain at least three vertices");
            CurrentVertexIndex = -1;
            CurrentEdgeIndex = -1;
            LastVertexIndex = -1;
            newPolygonVertices = new List<Vector2>();
            Dummy = new Mesh(Edges);
            DummySector = new SectorInfo();
            CurrentPolygon = new Polygon();
            RefreshListBox();
        }

        void CreateSector()
        {
            LastVertexIndex = -1;
            CurrentVertexIndex = -1;
            CurrentPolygon.Vertices = CurrentPolygon.GetVerticesCounterClockwise();
            CurrentPolygon.Triangulate();
            CurrentPolygon.AutoTriangulate = true;
            SelectedSector = new Sector(World, CurrentPolygon, CurrentFloor, CurrentCeiling);
            SelectedSector.Name = "sector" + World.Sectors.Count.ToString();
            World.Sectors.Add(SelectedSector);
            CurrentPolygon = new Polygon();
            World.FindWalls();
            RefreshListBox();
        }

        void AddSector(SectorInfo s)
        {
            Sectors.Add(s);
            AddPoly(new Polygon(s.Mesh.ToVertexList()));
        }

        void AddPoly(Polygon p)
        {
            Polys.Add(new PolygonColorTexture(p, Color.White, null));
            //Polys[Polys.Count - 1].FillBuffer();
            //p.AutoTriangulate = true;
            //p.Triangulate();
            //Polys.Add(p);
        }

        const float MAX = 0.70f * float.MaxValue; //Say whaaaaaaaaaaaat?!
        void SelectNearestWall(Vector2 p)
        {
            Vector2 distance = new Vector2(MAX);
            int m = -1;
            for (int i = 0; i < Edges.Count; i++)
			{
                if (DeletedEdges.Contains(i)) continue;
			    var item = Edges[i];
                foreach (var seg in item.ToSegments())
                {
                    var s = p - GeometryHelper.GetShortestVectorToLine(seg, p);
                    if (s.LengthSquared() < distance.LengthSquared())
                    {
                        m = i;
                        distance = s;
                    }
                }
            }
            if (distance.Length() < WallSelectRadius)
            {
                if (Keyboard.GetState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.LeftShift) ||
                    Keyboard.GetState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.RightShift))
                {
                    if (SelectedEdgesIndices.Contains(m))
                        SelectedEdgesIndices.Remove(m);
                    else
                        SelectedEdgesIndices.Add(m);
                }
                else 
                    CurrentEdgeIndex = m;
                RefreshListBox();
            }
        }

        void SelectSector(Vector2 p)
        {
            for (int i = 0; i < Polys.Count; i++)
            {
                if (Polys[i].IsInside(p))
                {
                    if (Keyboard.GetState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.LeftShift) ||
                    Keyboard.GetState().IsKeyDown(Microsoft.Xna.Framework.Input.Keys.RightShift))
                    {
                        if (SelectedSectorsIndices.Contains(i))
                            SelectedSectorsIndices.Remove(i);
                        else SelectedSectorsIndices.Add(i);
                    }
                    else
                    {
                        CurrentSectorIndex = i;
                    }
                    CurrentPolygon = Polys[i];
                    
                    RefreshListBox();
                    return;
                }
            }
            CurrentSectorIndex = -1;
        }

        void SelectVertex(Vector2 p)
        {
            float distance = float.MaxValue;
            int m = -1;
            for (int i = 0; i < Vertices.Count; i++)
            {
                if (DeletedVertices.Contains(i)) continue;
                float d = Vector2.Distance(Vertices[i], p);
                if (distance > d)
                {
                    distance = d;
                    m = i;
                }
            }
            CurrentVertexIndex = m;
            LastVertex = Vertices[m];
            LastVertexIndex = m;
        }

        bool SplitWalls(Vector2 p, bool createNewVertex)
        {
            int vi = -1;
            bool newVertex = false;
            if (Vertices.Contains(p))
            {
                vi = Vertices.IndexOf(p);
                if (DeletedVertices.Contains(vi)) DeletedVertices.Remove(vi);
            }
            else
            {
                newVertex = true;
                Vertices.Add(p);
                vi = Vertices.Count - 1;
            }
            bool found = false;
            foreach (var item in AliveEdges)
            {
                Edge e = Edges[item];
                if (e.Indices.Contains(vi)) continue;
                var segs = e.ToSegments();
                foreach (var s in segs)
                {
                    if (s.Contains(p))
                    {
                        //split
                        Edge e2 = new Edge(Vertices, vi, e.LastIndex, Wall.FromWall(e.Wall,
                            new LineSegment(Vertices[vi], e.LastVertex)));
                        Edges.Add(e2);
                        int e2i = Edges.Count - 1;
                        e.Indices[e.Indices.Count - 1] = vi;
                        e.Wall.EndPos = p;
                        for (int si = 0; si < Sectors.Count; si++)
                        {
                            var sector = Sectors[si];
                            if (sector.Mesh.Indices.Contains(item))
                            {
                                sector.Mesh.Indices.Insert(sector.Mesh.Indices.IndexOf(item) + 1, e2i);
                                Polys[si] = new PolygonColorTexture(new Polygon(sector.Mesh.ToVertexList()), Polys[si].Color, Polys[si].Texture);
                            }
                        }
                        found = true;
                        break;
                    }
                }
            }
            if (!found && !createNewVertex)
            {
                if (newVertex) Vertices.RemoveAt(vi);
            }
            else if (found)
            {
                RefreshListBox();
            }
            return found;
        }

        void MergeWalls()
        {
        }

        public void ReverseSelected()
        {
            switch (EditMode)
            {
                case EditModes.SelectEdge:
                    if (CurrentEdgeIndex < 0) return;
                    foreach (var e in SelectedEdgesIndices)
                    {
                        Edges[e].Reverse();    
                    }
                    break;
                case EditModes.SelectSector:
                    if (CurrentSectorIndex < 0) return;
                    foreach (var s in SelectedSectorsIndices)
                    {
                        for (int i = 0; i < Sectors[s].Mesh.Indices.Count; i++)
                        {
                            Edges[Sectors[s].Mesh.Indices[i]].Reverse();
                        }    
                    }
                    break;
                case EditModes.SelectVertex:
                    if (CurrentVertexIndex < 0) return;
                    foreach (var item in Edges)
                    {
                        if (item.Indices.Contains(CurrentVertexIndex))
                            item.Reverse();
                    }
                    break;
            }
        }
    }
}